package com.smh.yunpicturebackend.manager.upload;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.IdUtil;
import com.aliyun.oss.OSS;
import com.aliyun.oss.model.PutObjectRequest;
import com.smh.yunpicturebackend.config.property.OSSConfigProperties;
import com.smh.yunpicturebackend.exception.BusinessException;
import com.smh.yunpicturebackend.exception.ErrorCode;
import com.smh.yunpicturebackend.pojo.constant.FileConstant;
import com.smh.yunpicturebackend.pojo.dto.picture.UploadPictureResult;
import com.smh.yunpicturebackend.utils.AliyunOSSUtils;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.Resource;
import java.io.File;
import java.io.FileInputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * @author shiminghui
 * @date 2025/6/20 23:36
 * @description: 图片上传模板类
 */
@Slf4j
public abstract class PictureUploadTemplate {

    private static final DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");
    @Resource
    private OSSConfigProperties ossConfigProperties;
    @Resource
    private OSS ossClient;
    @Resource
    private AliyunOSSUtils aliyunOSSUtils;

    /**
     * 通用的上传图片逻辑
     *
     * @param prefix
     * @param inputSource
     * @return
     */
    public UploadPictureResult uploadPicture(String prefix, Object inputSource) {
        // 1. 校验图片格式是否有问题
        validPicture(inputSource);
        File tempFile = null;
        try {
            // 2. 创建临时文件存储图片
            tempFile = createTempFile(inputSource);

            // 3. 创建objectName
            String fileName = getFileName(inputSource);
            String objectName = generateObjectName(fileName, tempFile);
            if (objectName == null) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "objectName为空");
            }

            // 4. 上传图片
            PutObjectRequest request = new PutObjectRequest(
                    ossConfigProperties.getBucketName(),
                    prefix + "/" + objectName,
                    tempFile
            );
            ossClient.putObject(request);
            // 5. 获取图片信息
            String fileUrl = aliyunOSSUtils.getFileUrl(ossConfigProperties.getBucketName(), prefix + "/" + objectName);
            AliyunOSSUtils.BaseImageInfo baseImageInfo = aliyunOSSUtils.getBaseImageInfo(fileUrl);
            // 6. 获取图片平均颜色
            String picColor = aliyunOSSUtils.getImageAverageColor(fileUrl);

            UploadPictureResult pictureResult = new UploadPictureResult();
            pictureResult.setSourceUrl(fileUrl);
            pictureResult.setPicName(FileUtil.mainName(fileName));
            pictureResult.setPicSize(baseImageInfo.getFileSize());
            pictureResult.setPicWidth(baseImageInfo.getImageWidth());
            pictureResult.setPicHeight(baseImageInfo.getImageHeight());
            pictureResult.setPicScale((double) baseImageInfo.getImageHeight() / (double) baseImageInfo.getImageWidth());
            pictureResult.setPicFormat(baseImageInfo.getFormat());
            pictureResult.setPicColor(picColor);

            // 6. 返回结果
            return pictureResult;
        } catch (BusinessException be) {
            throw be;
        } catch (Exception e) {
            log.error("图片上传失败: {}", e.getMessage(), e);
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "图片上传失败");
        } finally {
            // 5. 删除临时文件
            deleteTempFile(tempFile);
        }
    }

    /**
     * 校验图片格式
     *
     * @param inputSource
     */
    protected abstract void validPicture(Object inputSource);

    /**
     * 创建临时文件
     *
     * @param inputSource
     * @return
     */
    protected abstract File createTempFile(Object inputSource);

    /**
     * 拿到原始文件名
     *
     * @param inputSource
     * @return
     */
    protected abstract String getFileName(Object inputSource);

    /**
     * 根据文件名生成唯一ObjectName
     *
     * @param fileName 文件名
     * @param tempFile 临时文件
     * @return String 文件名
     */
    public String generateObjectName(String fileName, File tempFile) {
        if (fileName == null) {
            return null;
        }
        // 生成日期文件路径
        String format = dateFormat.format(new Date());
        // 生成唯一文件名
        String uuid = IdUtil.simpleUUID();
        // 截取文件后缀
        int lastIndexOf = fileName.lastIndexOf(".");
        String fileSuffix = "";
        if (lastIndexOf != -1) {
            fileSuffix = fileName.substring(lastIndexOf);
            if (!FileConstant.IMAGE_SUFFIX_LIST.contains(fileSuffix)) {
                fileSuffix = "";
            }
        }
        if (fileSuffix.isEmpty()) {
            // 通过文件内容获取文件类型
            String s = parseFiletype(tempFile);
            if (s != null) {
                fileSuffix = "." + s.toLowerCase();
            }
        }
        if (fileSuffix.isEmpty()) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件格式错误");
        }

        return format + "/" + uuid + fileSuffix;
    }



    /**
     * 删除临时文件
     *
     * @param tempFile 临时文件
     */
    private void deleteTempFile(File tempFile) {
        if (tempFile != null && tempFile.exists()) {
            boolean delete = tempFile.delete();
            if (!delete) {
                log.warn("删除临时文件失败:{}", tempFile.getAbsolutePath());
            }
        }
    }


    /**
     * 获取文件类型
     *
     * @param file
     * @return
     */
    private String parseFiletype(File file) {
        try (FileInputStream fis = new FileInputStream(file)) {
            byte[] header = new byte[12]; // 读取文件头
            int read = fis.read(header);

            if (read < 8) {
                return null; // 文件头过短
            }

            // JPEG - 以 FF D8 开头
            if ((header[0] & 0xFF) == 0xFF &&
                    (header[1] & 0xFF) == 0xD8) {
                return "JPEG";
            }

            // PNG - 头8字节特殊序列
            if (header[0] == (byte) 0x89 &&
                    header[1] == (byte) 0x50 &&
                    header[2] == (byte) 0x4E &&
                    header[3] == (byte) 0x47 &&
                    header[4] == (byte) 0x0D &&
                    header[5] == (byte) 0x0A &&
                    header[6] == (byte) 0x1A &&
                    header[7] == (byte) 0x0A) {
                return "PNG";
            }

            // GIF - 头6字节为"GIF87a"或"GIF89a"
            if (header[0] == 'G' &&
                    header[1] == 'I' &&
                    header[2] == 'F' &&
                    header[3] == '8' &&
                    (header[4] == '7' || header[4] == '9') &&
                    header[5] == 'a') {
                return "GIF";
            }

            // BMP - 头2字节为"BM"
            if (header[0] == 'B' && header[1] == 'M') {
                return "BMP";
            }

            // WEBP - 需检查头12字节
            if (read >= 12 &&
                    header[0] == 'R' && header[1] == 'I' && header[2] == 'F' && header[3] == 'F' &&
                    header[8] == 'W' && header[9] == 'E' && header[10] == 'B' && header[11] == 'P') {
                return "WEBP";
            }

            // TIFF - 小端序(II)或大端序(MM)
            if (header[0] == 'I' && header[1] == 'I' && header[2] == 0x2A && header[3] == 0x00) {
                return "TIFF";
            }
            if (header[0] == 'M' && header[1] == 'M' && header[2] == 0x00 && header[3] == 0x2A) {
                return "TIFF";
            }

            return null;
        } catch (Exception e) {
            log.error("解析文件类型失败,错误为:{}", e.getMessage(), e);
            return null;
        }
    }

}
